package com.futu.openapi;

import com.google.protobuf.GeneratedMessageV3;
import com.sun.jna.CallbackThreadInitializer;
import com.sun.jna.Native;
import com.sun.jna.Pointer;


public class FTAPI_Conn implements AutoCloseable {
    public final int INIT_FAIL = 100;

    protected Pointer channel;
    private FTSPI_Conn connSpi;
    private FTAPIChannel_OnInitConnectCallback initConnectCallback = new FTAPIChannel_OnInitConnectCallback() {
        @Override
        public void invoke(Pointer client, long errCode, String strDesc) {
            FTAPI_Conn.this.onInitConnect(errCode, strDesc);
        }
    };

    private FTAPIChannel_OnDisConnectCallback disConnectCallback = new FTAPIChannel_OnDisConnectCallback() {
        @Override
        public void invoke(Pointer client, long errCode) {
            FTAPI_Conn.this.onDisconnect(errCode);
        }
    };

    private FTAPIChannel_OnReplyCallback replyCallback = new FTAPIChannel_OnReplyCallback() {
        @Override
        public void invoke(Pointer client, int enReqReplyType, ProtoHeader protoHeader, Pointer pProtoData, int nDataLen) {
            if (nDataLen > 0 && pProtoData != Pointer.NULL) {
                FTAPI_Conn.this.onReply(ReqReplyType.fromCode(enReqReplyType), protoHeader, pProtoData.getByteArray(0, nDataLen));
            } else {
                FTAPI_Conn.this.onReply(ReqReplyType.fromCode(enReqReplyType), protoHeader, null);
            }
        }
    };

    private FTAPIChannel_OnPushCallback pushCallback = new FTAPIChannel_OnPushCallback() {
        @Override
        public void invoke(Pointer client, ProtoHeader protoHeader, Pointer pProtoData, int nDataLen) {
            FTAPI_Conn.this.onPush(protoHeader, pProtoData.getByteArray(0, nDataLen));
        }
    };

    public FTAPI_Conn() {
        channel = FTCAPI.INSTANCE.CreateFTAPIChannel();
        FTCAPI.INSTANCE.FTAPIChannel_SetProgrammingLanguage(channel, "Java");
        CallbackThreadInitializer cbThreadInitializer = new CallbackThreadInitializer(true, false);
        Native.setCallbackThreadInitializer(replyCallback, cbThreadInitializer);
        Native.setCallbackThreadInitializer(pushCallback, cbThreadInitializer);
        Native.setCallbackThreadInitializer(initConnectCallback, cbThreadInitializer);
        Native.setCallbackThreadInitializer(disConnectCallback, cbThreadInitializer);
        FTCAPI.INSTANCE.FTAPIChannel_SetOnInitConnectCallback(channel, initConnectCallback);
        FTCAPI.INSTANCE.FTAPIChannel_SetOnDisconnectCallback(channel, disConnectCallback);
        FTCAPI.INSTANCE.FTAPIChannel_SetOnReplyCallback(channel, replyCallback);
        FTCAPI.INSTANCE.FTAPIChannel_SetOnPushCallback(channel, pushCallback);
    }

    synchronized public void setConnSpi(FTSPI_Conn callback) {
        connSpi = callback;
    }

    /***
     * 释放底层资源
     */
    synchronized public void close() {
        if (channel != null) {
            FTCAPI.INSTANCE.ReleaseFTAPIChannel(channel);
            channel = null;
        }
    }

    /***
     * 断开与OpenD的连接
     * @return 是否成功
     */
    synchronized public boolean disconnect() {
        if (channel != null) {
            return FTCAPI.INSTANCE.FTAPIChannel_Close(channel) == 0;
        }
        return false;
    }

    /***
     * 设置客户端信息，用于记录
     * @param clientID 客户端名字
     * @param clientVer 客户端版本
     */
    synchronized public void setClientInfo(String clientID, int clientVer) {
        if (channel != null) {
            FTCAPI.INSTANCE.FTAPIChannel_SetClientInfo(channel, clientID, clientVer);
        }
    }

    /***
     * 设置加密私钥
     * @param key
     */
    synchronized public void setRSAPrivateKey(String key) {
        if (channel != null) {
            FTCAPI.INSTANCE.FTAPIChannel_SetRSAPrivateKey(channel, key);
        }
    }

    /**
     * @param ip              地址
     * @param port            端口
     * @param isEnableEncrypt 启用加密
     * @return bool 是否启动了执行，不代表连接结果，结果通过OnInitConnect回调
     * @brief 初始化连接，连接并初始化
     */
    synchronized public boolean initConnect(String ip, short port, boolean isEnableEncrypt) {
        if (channel != null) {
            int ret = FTCAPI.INSTANCE.FTAPIChannel_InitConnect(channel, ip, port, isEnableEncrypt ? 1 : 0);
            return ret == 0;
        }
        return false;
    }

    /**
     * 此连接的连接ID，连接的唯一标识，InitConnect协议返回，没有初始化前为0
     *
     * @return
     */
    synchronized public long getConnectID() {
        if (channel != null) {
            return FTCAPI.INSTANCE.FTAPIChannel_GetConnectID(channel);
        }
        return 0;
    }

    synchronized protected int sendProto(int protoID, GeneratedMessageV3 req) {
        if (channel != null) {
            byte[] data = req.toByteArray();
            return FTCAPI.INSTANCE.FTAPIChannel_SendProto(channel, protoID, (byte) 0, data, data.length);
        }

        return 0;
    }

    synchronized protected void onReply(ReqReplyType replyType, ProtoHeader protoHeader, byte[] data) {

    }

    synchronized protected void onPush(ProtoHeader protoHeader, byte[] data) {

    }

    synchronized private void onInitConnect(long errCode, String desc) {
        if (connSpi != null) {
            connSpi.onInitConnect(this, errCode, desc);
        }
    }

    synchronized private void onDisconnect(long errCode) {
        if (connSpi != null) {
            connSpi.onDisconnect(this, errCode);
        }
    }
}
